
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "emulate.h"
#include <mysql.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>


static nfc_device_t *nfc;
unsigned char Ndef[MAX_PDU_LEN];
char* TABLE = (char*)"1";
unsigned long sequence = 1;
//char* url = (char*)"http://86.50.142.140:8090/mn.php?s=";
char* url = (char*)"http://82.130.7.116:8090/mn.php?seqNum=";

unsigned char* key = (unsigned char*)"abcdefghiklmnopq";

unsigned char selectNdefTagApp[] = {0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00};
size_t szSelectNdefTagApp = 13;

unsigned char selectCC[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x03};
size_t szSelectCC = 7;

unsigned char readCmd[] = {0x00, 0xB0, 0x00, 0x00, 0x0F};
size_t szRead = 5;

unsigned char selectNdef[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04};
size_t szNdefSelect = 7;

unsigned char readNdefLen[] = {0x00, 0xB0, 0x00, 0x00, 0x02};
size_t szReadNdefLen = 5;

unsigned char tmp[] = {0x00, 0x0F, 0x20, 0x00, 0x54, 0x00, 0xFF, 0x04, 0x06, 0xE1, 0x04, 0xFF, 0xFE,
													0x00, 0x00, 0x90, 0x00};


//connect to the device
int tryToConnect(){
	int rdrRetry;
	for (rdrRetry = 0; rdrRetry < 5; rdrRetry++){
	    nfc = nfc_connect (NULL);
	    if (nfc)
	      return EXIT_SUCCESS;
	}
	if (!nfc) {
		printf("Unable to connect to NFC reader. \n");
		return EXIT_FAILURE;
	}
	return EXIT_FAILURE;
}

bool isSelectCommand(unsigned char* cmd, size_t cmdSize){
	if (cmdSize > 2){
		if ((cmd[0] == (byte_t)0x00) && (cmd[1] == (byte_t) 0xa4)){
			return true;
		}
	}
	return false;
}
//construct url
void constructURL(int seq, unsigned char* url_seq){
	unsigned char buffer[10];

	unsigned char seqNum[10];
	sprintf((char*)seqNum, "%lu", sequence);

	sprintf((char*)buffer, "%lu", sequence);
	strcat((char*)buffer, TABLE);

	printf("buffer = %s \n", buffer);
	unsigned int result_len = 16;
	unsigned char* mac_seq_binary = (unsigned char*) malloc(result_len);
	unsigned char* mac_seq_hex = (unsigned char*) malloc(result_len * 2);

	HMAC_CTX ctx;

	ENGINE_load_builtin_engines();
	ENGINE_register_all_complete();

	HMAC_CTX_init(&ctx);
	HMAC_Init_ex(&ctx, key, 16, EVP_md5(), NULL);
	HMAC_Update(&ctx, buffer, strlen((char*)buffer));
	HMAC_Final(&ctx, mac_seq_binary, &result_len);
	HMAC_CTX_cleanup(&ctx);
	int i;
	//produce a hex sequence of the hash
	for(i = 0; i < result_len; i++){
		sprintf(mac_seq_hex + i * 2, "%02x", mac_seq_binary[i]);
	}

	//build the url
	//sprintf((char*)url_seq, seq);
	strcpy((char*)url_seq, url);
	strcat((char*)url_seq, (char*)seqNum);
	strcat((char*)url_seq, "&s=");
	strcat((char*)url_seq, (char*)mac_seq_hex);
	strcat((char*)url_seq, "&t=");
	strcat((char*)url_seq, TABLE);

	printf("url is \n");
	printf("%s \n", url_seq);

	printf("\n");
	free (mac_seq_binary);
	free(mac_seq_hex);
}
//emulate the tag
int main (int argc, char *argv[])
{
	if(tryToConnect() == EXIT_FAILURE)
		return 0;

	unsigned char commandApdu[MAX_PDU_LEN];
	unsigned char responseApdu[MAX_PDU_LEN];

	nfc_target_t target = {
		.nm.nmt = NMT_ISO14443A, //nfc_modulation_t
		.nm.nbr = NBR_UNDEFINED, // will be updated by nfc_target_init()
		.nti.nai.abtAtqa = {0x00, 0x04}, // nfc_target_info_t
		.nti.nai.abtUid = {0x08, 0x53, 0x41, 0x4E},
		.nti.nai.btSak = 0x20,
		.nti.nai.szUidLen = 4,
		.nti.nai.szAtsLen = 0,
	};

	printf ("Connected to NFC device: %s\n", nfc->acName);
	printf ("Emulating NDEF tag now, please touch it with a second NFC device\n");

	bool sessionTerminated;

	while(1){
		sessionTerminated = false;

		size_t szCommand, szResponse;

		if (!nfc_target_init(nfc, &target, commandApdu, &szCommand)){
			nfc_perror(nfc, "nfc_target_init");
		}

		else{
			//calculate the MAC of sequence number using the shared key
			//create NDEF message from the newly-created URL
			unsigned char* url_seq = (unsigned char*)malloc(100);
			constructURL(sequence, url_seq);

			int len = produceNdefFromURI((char*)url_seq, Ndef);

			sequence++;
			printf("sequence = %lu\n", sequence);

			sessionTerminated = false;

			while(!sessionTerminated){
				if (!nfc_target_receive_bytes(nfc, commandApdu, &szCommand)){
					nfc_perror(nfc, "nfc_target_receive_bytes");

					//if this is "Tag Released event"
					if (nfc->iLastError == (byte_t)0x29)
						sessionTerminated = true;
				}
				else{
					printf("Received data (%d):", (int)szCommand);
					print_hex(commandApdu, szCommand);

					bool isSelectCmd = isSelectCommand(commandApdu, szCommand);
					bool isReadCmd = hex_cmp(commandApdu, szCommand, readCmd, szRead);
					bool isReadNdefLen = hex_cmp(commandApdu, szCommand, readNdefLen, szReadNdefLen);

					int len_ReadNdefFile = (int)Ndef[1] + 2;
					unsigned char readNdefFile[] = {0x00, 0xb0, 0x00, 0x00, (byte_t)len_ReadNdefFile};
					size_t szReadNdefFile = 5;

					bool isReadNdefFile = hex_cmp(commandApdu, szCommand, readNdefFile, szReadNdefFile);

					if (isSelectCmd){
						//send response
						responseApdu[0] = 0x90;
						responseApdu[1] = 0x00;
						szResponse = 2;
						printf("Sending data (%d):", (int)szResponse);
						print_hex(responseApdu, szResponse);
					}
					else if(isReadCmd){
						int i;
						//0x54 here means the maximum data could be read
						unsigned char tmp[] = {0x00, 0x0F, 0x20, 0x00, 0x54, 0x00, 0xFF, 0x04, 0x06, 0xE1, 0x04, 0xFF, 0xFE,
																		0x00, 0x00, 0x90, 0x00};
						memcpy(responseApdu, tmp, 17);
						szResponse = 17;
						printf("Sending data (%d):", (int)szResponse);
						print_hex(responseApdu, szResponse);
					}
					else if (isReadNdefFile){ //send out the NDEF content
						szResponse = len;
						memcpy(responseApdu, Ndef, szResponse);
						printf("Sending data (%d):", (int)szResponse);
						print_hex(responseApdu, szResponse);

					}
					else if (isReadNdefLen){
						//00  16  90  00
						responseApdu[0] = 0x00;
						responseApdu[1] = (byte_t)len_ReadNdefFile;
						responseApdu[2] = 0x90;
						responseApdu[3] = 0x00;
						szResponse = 4;
						printf("Sending data (%d):", (int)szResponse);
						print_hex(responseApdu, szResponse);
					}
					else{
						// Unknown command received.
						responseApdu[0] = 0x6A;
						responseApdu[1] = 0x81;
						szResponse = 2;
						printf("unknown command");
						sessionTerminated = true;
						break;
					}

					if (!nfc_target_send_bytes(nfc, responseApdu, szResponse)) {
						nfc_perror(nfc, "nfc_target_send_bytes");
						sessionTerminated = true;
					}
				}
			}
			free(url_seq);
		}
	}
}
